package com.whjz.utils;

import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;


/**
 * 非对称加解密算法工具类
 *
 * 目前只支持RSA算法。
 *
 * @author kui.lijk
 * @version $Id: rsaCryptoUtil.java, v 0.1 2014-11-14 下午7:57:33 kui.lijk Exp $
 */
public class RSACryptoUtil {

    /**
     * The constant RSA.
     */
    public static final String RSA            = "RSA";

    /**
     * The constant ALGO_RSA_PKCS1.
     */
    public static final String ALGO_RSA_PKCS1 = "RSA/ECB/PKCS1Padding";

    /**
     * The constant ALGO_RSA_NONE.
     */
    public static final String ALGO_RSA_NONE  = "RSA/ECB/NoPadding";

    /**
     * 非对称加解密(/ECB/PKCS1Padding模式)
     * 本方法是RSA算法的公钥加密 私钥解密.
     *
     * @param text 待加/解密的数据.
     * @param keyData 密钥数据.
     * @param mode 加解密标识：加密——Cipher.ENCRYPT_MODE；解密——Cipher.DECRYPT_MODE
     * @return 密文(加密) /明文（解密）.
     * @throws GeneralSecurityException the general security exception
     */
    public static byte[] rsaCrypto(final byte[] text, final byte[] keyData, final int mode)
            throws GeneralSecurityException {

        return rsaCrypto(text, keyData, ALGO_RSA_PKCS1, mode);
    }

    /**
     * 非对称加解密(/ECB/PKCS1Padding模式)
     * 本方法是RSA算法的公钥加密 私钥解密.
     *
     * @param text 待加/解密的数据.
     * @param keyData 密钥数据.
     * @param algorithm 非对称加密算法名称。目前只接受RSA.
     * @param mode 加解密标识：加密——Cipher.ENCRYPT_MODE；解密——Cipher.DECRYPT_MODE
     * @return 密文(加密) /明文（解密）.
     * @throws GeneralSecurityException the general security exception
     */
    public static byte[] rsaCrypto(final byte[] text, final byte[] keyData, final String algorithm,
                                   final int mode) throws GeneralSecurityException {

        final Cipher cipher = getCipher(keyData, algorithm, mode);
        return cipher.doFinal(text);
    }

    /**
     * 非对称加解密(/ECB/PKCS1Padding模式)
     * 本方法是RSA算法的私钥加密 公钥解密.
     *
     * @param text 待加/解密的数据.
     * @param keyData 密钥数据.
     * @param mode 加解密标识：加密——Cipher.ENCRYPT_MODE；解密——Cipher.DECRYPT_MODE
     * @return 密文(加密) /明文（解密）.
     * @throws GeneralSecurityException the general security exception
     */
    public static byte[] rsaCryptoReverse(final byte[] text, final byte[] keyData, final int mode)
            throws GeneralSecurityException {

        return rsaCryptoReverse(text, keyData, ALGO_RSA_PKCS1, mode);
    }

    /**
     * 非对称加解密(/ECB/PKCS1Padding模式)
     * 本方法是RSA算法的私钥加密 公钥解密.
     *
     * @param text 待加/解密的数据.
     * @param keyData 密钥数据.
     * @param algorithm 非对称加密算法名称。KMI目前只接受RSA.
     * @param mode 加解密标识：加密——Cipher.ENCRYPT_MODE；解密——Cipher.DECRYPT_MODE
     * @return 密文(加密) /明文（解密）.
     * @throws GeneralSecurityException the general security exception
     */
    public static byte[] rsaCryptoReverse(final byte[] text, final byte[] keyData,
                                          final String algorithm, final int mode)
            throws GeneralSecurityException {

        final Cipher cipher = getCipherReverse(keyData, algorithm, mode);
        return cipher.doFinal(text);
    }

    /**
     * 得到公钥
     *
     * @param keyData 密钥数据
     * @return the public key
     * @throws GeneralSecurityException the general security exception
     */
    private static PublicKey getPublicKey(final byte[] keyData) throws GeneralSecurityException {
        final X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyData);
        final KeyFactory keyFactory = KeyFactory.getInstance(RSA);
        return keyFactory.generatePublic(keySpec);
    }

    /**
     * 得到私钥
     *
     * @param keyData 密钥数据
     * @return the private key
     * @throws GeneralSecurityException the general security exception
     */
    private static PrivateKey getPrivateKey(final byte[] keyData) throws GeneralSecurityException {
        final PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyData);
        final KeyFactory keyFactory = KeyFactory.getInstance(RSA);
        return keyFactory.generatePrivate(keySpec);
    }

    /**
     * 根据参数初始化cipher的方法(默认是公钥加密私钥解密)
     *
     * @param keyData the key data
     * @param algorithm the algorithm
     * @param mode the mode
     * @return cipher cipher
     * @throws GeneralSecurityException the general security exception
     */
    private static Cipher getCipher(final byte[] keyData, final String algorithm, final int mode)
            throws GeneralSecurityException {
        if (mode != Cipher.ENCRYPT_MODE && mode != Cipher.DECRYPT_MODE) {
            throw new GeneralSecurityException(
                    "Only support Cipher.ENCRYPT_MODE和Cipher.DECRYPT_MODE, real:" + mode);
        }

        Cipher cipher = Cipher.getInstance(algorithm);

        Key rsaKey;
        if (mode == Cipher.ENCRYPT_MODE) {
            rsaKey = getPublicKey(keyData);
            cipher.init(mode, rsaKey);
        } else {
            rsaKey = getPrivateKey(keyData);
            cipher.init(mode, rsaKey);
        }
        return cipher;
    }

    /**
     * 根据参数初始化cipher的方法(私钥加密公钥解密)
     *
     * @param keyData the key data
     * @param algorithm the algorithm
     * @param mode the mode
     * @return cipher reverse
     * @throws Exception the general security exception
     */
    private static Cipher getCipherReverse(final byte[] keyData, final String algorithm,
                                           final int mode) throws GeneralSecurityException {
        if (mode != Cipher.ENCRYPT_MODE && mode != Cipher.DECRYPT_MODE) {
            throw new GeneralSecurityException(
                    "Only supports: Cipher.ENCRYPT_MODE和Cipher.DECRYPT_MODE, real:" + mode);
        }

        Cipher cipher;
        cipher = Cipher.getInstance(algorithm);
        Key rsaKey;
        if (mode == Cipher.ENCRYPT_MODE) {
            rsaKey = getPrivateKey(keyData);
            cipher.init(mode, rsaKey);
        } else {
            rsaKey = getPublicKey(keyData);
            cipher.init(mode, rsaKey);
        }
        return cipher;
    }

}
